package com.adee.java;


import com.adee.util.Utils;

/**
 * https://blog.csdn.net/Prior_SX/article/details/123463430
 */
public class Test005_String {
    public static void main(String[] args) throws InterruptedException {
//        test00();
//        test01();
        test1();
//        test3();
//        test4();
    }

    /**
     * String.intern方法：
     * 1. intern()方法在jdk6和jdk(7/8)的区别
     *
     * （1）在jdk6中，字符串常量池在永久代，调用intern()方法时，若常量池中不存在等值的字符串，JVM就会在字符串
     *     常量池中创建一个等值的字符串，然后返回该字符串的引用；
     *
     * （2）在jdk7/8中，字符串常量池被移到了堆空间中，调用intern()方法时，如果常量池已经存在该字符串，则直接返
     *     回字符串引用，否则复制该堆空间中字符串对象到常量池中并返回。
     *
     * 2. 该方法加锁，会影响性能，如果字符串重复率不高，不要使用该方法；
     * 3. 该方法主要适用于程序中只存在有限个重复被使用的字符串场景，由于字符串是不可变的（只可读不可写），所以可以
     *    重复使用，可以减少内存消耗。
     * */
    public static void test00() {
        /**
         * 回答问题
         * （1）str1这行代码执行的过程中创建了几个对象？
         * （2）str2这行代码执行的过程中创建了几个对象？
         *
         * （1）str1这行中创建了两个对象：一个是在字符串池中的"ab"对象，另一个是new String() new出来的对象。
         *  (2) str2这行创建了5个对象，
         *      字符串池中的"a"和"b"，
         *      new出来的String("a")和String("b")，
         *      +号编译为Stringbuilder.append(String("a")).append(String("b")).toString()得到的对象。
         *      共5个对象。
         */
        String str1 = new String("ab");
        String str2 = new String("a") + new String("b");
        System.out.println(str1);
        System.out.println(str2);
    }

    /**
     *1 、在jdk6中，结果为 false false
     * 原因：
     * （1）执行完第一行代码，会创建两个对象：堆空间的new String ()和字符串常量池（jdk6中字符串常量池在永久代）中的“1”；这个时候字符串常量池中是存在“1”的；所以执行 s.intern()；直接返回“1”的引用。执行 String s2 =“1”;由于常量池中已经存在“1”，所以s2直接指向常量池中“1”的引用 !
     * 所以，s2的地址是字符串常量池中的“1”，s的地址在堆空间，二者并不相等！结果为false
     *
     * (2)上面分析过，s3存在于堆空间中，由于在jdk6中字符串常量池在永久代中，执行s3.intern()时，相当于创建了一个新的对象”11“，所以产生新地址！s4则是在字符串常量池中，所以s3、s4并不相等，结果为false!
     * 2 、在jdk7/8中，结果为false true
     * 原因：
     * （1）在jdk7/8中除了字符串常量池的位置不一样，false的原因和jdk6一样
     * （2）这是一个字符串拼接操作，上文已经分析过，执行完第一行代码，会创建6个对象，但是这个“11”是存在于堆空间中的，
     * 字符串常量池中并没有“11‘，然后执行 s3.intern();在jdk7/8中，字符串常量池移到了堆空间中，为了节省空间，优化内存，
     * 他会创建一个指向堆空间中new String(“11”)的地址。所以执行完 s3.intern();此时的字符串常量池中的”11“是引用堆空间
     * 的new String(“11”)的地址。 当执行String s4 = "11"时， s4会从常量池中找到”引用地址“，所以这样看来，
     * s4 指向”引用地址“，”引用地址“指向堆空间中的new String(“11”)，而s3本来就指向堆空间中的new String(“11”)，
     * 二者相等，结果为true!
     *
     */
    // 静态变量在类加载连接后，初始化时，根据字面量"11"创建一个字符串对象，并放入字符串池中，字符串池在jvm中只有一个，全局共享的
    public static String ss = "11";
    public static void test01() throws InterruptedException {
        String s = new String("1");
        //s.intern();
        String s2 ="1";
        System.out.println(s == s2);// false
        //TimeUnit.MINUTES.sleep(10);
        String s3 = new String("1") + new String("1");
        // new StringBuilder().toString();
        // intern方法中，先检查字符串池中有没有"11"对象，有，因为在上面的静态变量ss初始化时已经创建了"11"对象，并放入到字符串池中了
        String s3_0 = s3.intern();
        //方法中的字符串字面量不会在类加载连接初始化后放到字符串池中，只有在运行时执行到
        // 该行代码时才会将字面量创建成一个对象，并放入字符串池
        String s4 = "11";
        System.out.println(s3 == s4); // false
    }
    public static void test1() {
        String s = "Love You";
        String s2 = "Love"+" You";
        String s3 = s2 + "";
        String s4 = new String("Love You");
        // new StringBuilder(String s) 时，内部创建一个新的 byte[] value，然后将字符串s内部的字节数组拷贝到value中
        String s5 = (new StringBuilder(String.valueOf(s2))).toString();
        s5.equals(null);
        // 编译优化为 String s6 = "hello2";
        String s6 = "hello" + 2;
        String s7 = "hello2";
        String s8 = "hello";
        // String s9 = s8 + 2; 编译为 String s9 = new StringBuilder().append(s8).append('\002').toString(); 得到新的字符串对象
        String s9 = s8 + 2;

        System.out.println("s == s2 "+(s==s2)); // true
        System.out.println("s == s3 "+(s==s3)); // false
        System.out.println("s == s4 "+(s==s4)); // false
        System.out.println("s == s3.intern() " + (s==s3.intern())); // true
        System.out.println("s3.intern() == s4.intern() " + (s3.intern() == s4.intern())); // true
        System.out.println("s3.intern() == s.intern() " + (s3.intern() == s.intern())); // true
        System.out.println("s == s.intern() " + (s == s.intern())); // true
        System.out.println("s6 == s7 " + (s6 == s7)); // true
        System.out.println("s7 == s9 " + (s7 == s9)); // false
        System.out.println("s7 == Utils.hello2 " + (s7 == Utils.hello2)); // true 整个jvm只有一个字符串常量池，所有代码共享
    }

    /*
     * String、StringBuilder、StringBuffer 对比
     * String是不可变的
     * StringBuilder可变，通过操作成员变量 byte[] value;来改变字符串
     * StringBuffer就是StringBuilder的synchronized版
     * */
    public static void test2() {
        StringBuilder builder = new StringBuilder();
        StringBuffer buffer = new StringBuffer();
    }

    public static void test3() {
        String s = new String("1");
        //Utils.sleep(10000);
        String s2 = "1";
        s.intern();
        System.out.println(s == s2); // false
        /**
         * new String("1") + new String("1") 编译后为：
         * new StringBuilder().append(new String("1")).append(new String("1")).toString()
         * 返回一个新的String对象，但是该对象不在字符串常量池创建字符串常量。
         */
        String s3 = new String("1") + new String("1");
        String s4 = "11";
        s3.intern();
        System.out.println(s3 == s4); // false
        System.out.println(s4 == s3.intern()); // true
    }


    public static void test4() {
        String s = new String("1");
        //Utils.sleep(10000);
        String s2 = "1";
        s.intern();
        System.out.println(s == s2); // false

        String s3 = new String("1") + new String("1");
        s3.intern();
        String s4 = "11";
        System.out.println(s3 == s4); // false
        System.out.println(s4 == s3.intern()); // true
    }

    //
    public static void test5() {
        Integer.parseInt("");
    }


}
